################################################### # Raspberry Pi based Shooting Gallery Project # # OS and Program Setup Guide # # By: Mike Young -- July 7, 2025 # ################################################### Download Raspberry Pi OS Lite... https://www.raspberrypi.com/software/operating-systems/ I used... Release date: May 13th 2025 System: 32-bit Kernel version: 6.12 Debian version: 12 (bookworm) As of July 3, 2025, that can be downloaded from... https://downloads.raspberrypi.com/raspios_lite_armhf/images/raspios_lite_armhf-2025-05-13/2025-05-13-raspios-bookworm-armhf-lite.img.xz Extract the .xz file using 7-Zip (assuming you're using Windows). Image SD card with Win32 Disk Imager, using a 16GB SD card (again, assumes you're on Windows), plug into the Raspberry Pi 3 A+ (other models can certain work as well), and power it on. You can get Win32 Disk Imager from... https://sourceforge.net/projects/win32diskimager/ Power the Raspberry Pi on and once logged in and initial setup tasks completed, run... sudo raspi-config ...and configure it as follows (assumes US settings)... System Options > Audio > 0 bcm2835 Headphones > Hostname > shootinggallery (or whatever hostname you want) > Auto login > Yes Interface > SSH > Yes Localisation Options > Locale > unstar "en_GB.UTF-8 UTF-8" and star "en_US.UTF-8 UTF-8", then select "None" > Timezone > set accordingly > Keyboard > Generic 105-key PC, English (US), The default for the keyboard layout > No compose key > WLAN Country > US United State Select Finish, then reboot. When the system reboots and logs back in, run... sudo nmtui Activate a connection. Select your wireless network and enter the password. Select OK. Verify that it connected. Tab to Back, then Quit. Ping something on your network, such as your gateway or Google DNS, 8.8.8.8, to verify Internet connectivity. Run... ip addr ...and get the IP address of the Raspberry Pi, or determine the IP address using your router's client list. Connect to the unit IP address or hostname using PuTTY (assumes Windows), or your preferred SSH client. You can get PuTTY from... https://www.putty.org/ Once logged in, run... sudo apt update I encountered message (can be ignored)... W: http://raspbian.raspberrypi.com/raspbian/dists/bookworm/InRelease: Key is stored in legacy trusted.gpg keyring (/etc/apt/trusted.gpg), see the DEPRECATION section in apt-key(8) for details. Run... nano ~/.nanorc ...add this line (makes editing source code in GNU nano easier from my perspective)... set tabsize 4 ...and save the file. Run... nano ~/.profile ...and add the following... ################################# if [ -z "$SSH_CONNECTION" ]; then if [ -b /dev/sda1 ]; then sudo mount -o uid=user,gid=user /dev/sda1 /mnt/usb fi sudo ./game fi ################################# ...to the bottom and save that file by pressing [Ctrl] + [X], [Y], [Enter]. Create the /mnt/usb folder with this command... sudo mkdir /mnt/usb Install festival-lite with... sudo apt install flite Install system dependencies required for building Python and PyAudio... sudo apt install python3-pip python3-venv python3-full portaudio19-dev Create a Python virtual environment in your home directory... python3 -m venv ~/vosk_env Activate the virtual environment... source ~/vosk_env/bin/activate Upgrade pip inside the virtual environment (recommended)... pip install --upgrade pip Install required Python packages inside the virtual environment (note that numpy takes a while to build from source so this command will take quite a while to complete)... pip install vosk PyAudio numpy Deactivate the virtual environment... deactivate Download the Vosk small model, unzip it, and move it to folder "model"... wget https://alphacephei.com/vosk/models/vosk-model-small-en-us-0.15.zip && unzip vosk-model-small-en-us-0.15.zip && mv vosk-model-small-en-us-0.15 model Create the recognize.py Python script with... nano recognize.py ...paste the following into that file... ####################################### from vosk import Model, KaldiRecognizer import pyaudio import json import numpy as np import os import time def amplify(data, dB): factor = 10 ** (dB / 20) audio = np.frombuffer(data, dtype=np.int16) boosted = np.clip(audio * factor, -32768, 32767).astype(np.int16) return boosted.tobytes() model = Model("model") rec = KaldiRecognizer(model, 48000) rec.SetWords(False) p = pyaudio.PyAudio() stream = p.open(format=pyaudio.paInt16, channels=1, rate=48000, input=True, input_device_index=1, frames_per_buffer=8000) stream.start_stream() print("Listening with pause control...") pause_file = "/dev/shm/pause_recognition" while True: # Check if pause flag file exists if os.path.exists(pause_file): time.sleep(0.1) #print("Paused...") continue data = stream.read(4000, exception_on_overflow=False) amplified = amplify(data, 30) if rec.AcceptWaveform(amplified): result = json.loads(rec.Result()) text = result.get("text", "").strip() print(f"? Final: {text}") # Debug final output if text: with open("/dev/shm/input.txt", "w") as f: f.write(text) else: partial = json.loads(rec.PartialResult()).get("partial", "").strip() if partial: print(f"? Partial: {partial}") # Debug partial input ################################################################# ...and save that file. Connect light sensors to the GPIO as follows (or see GPIO connections picture)... top corner of board, pins 2 and 4 are 5V power and aren't used ------------------------x | /---\ | | | <-----|---- PCB mounting hole \---/ | | VCC1 [ 1] [ 2] | VCC1 (pin 1) is 3.3V power for a bank of 4 sensors, daisy-chained | [ 3] [ 4] | | [ 5] [ 6] | | [ 7] [ 8] | | GND1 [ 9] [10] | GND1 (pin 9) is ground for a bank of 4 sensors, daisy-chained | 0 [11] [12] | GPIO17 (pin 11) runs to light sensor 0 | 1 [13] [14] | GPIO27 (pin 13) runs to light sensor 1 | 2 [15] [16] 6 | GPIO22 (pin 15) runs to light sensor 2 and GPIO23 (pin 16) runs to light sensor 6 | VCC2 [17] [18] 7 | VCC2 (pin 17) is 3.3V power for a bank of 4 sensors, daisy-chained, and GPIO24 (pin 18) runs to light sensor 7 | [19] [20] | | [21] [22] | | [23] [24] | | [25] [26] | | [27] [28] | | 3 [29] [30] | GPIO5 (pin 29) runs to light sensor 3 | 4 [31] [32] | GPIO6 (pin 31) runs to light sensor 4 | [33] [34] | | [35] [36] | | 5 [37] [38] | GPIO26 (pin 37) runs to light sensor 5 | GND2 [39] [40] | GND2 (pin 39) is ground for a bank of 4 sensors, daisy-chained | | Connect to the system via SFTP client and upload gameX.c to it. On Windows I prefer WinSCP, which you can get from... https://winscp.net/eng/download.php Compile and run gameX.c with this gcc command (now linking the math library with -lm switch as the math.h library now used for the pow function)... gcc -o game gameX.c -lpigpio -lrt -lpthread -lm && sudo ./game ############################ # Basic Game Operation # ############################ Program starts. Program prompts for shooting distance. If you don't say anything it will use its saved value. Say "no" and you'll be prompted to say a new distance. If it mishears you it will use 30 feet. Place/cycle target 1-8 to select game mode 1-8. Game modes are as follows... Game Mode 1 =========== 1 player mode. The object is to hit all 8 targets as quickly as possible. When you finish it the game will announce your time. There is no time limit. Game Mode 2 =========== 1 player mode. The object is to hit all 8 targets as quickly as possible. Points per target are the same, but as time runs the targets are worth fewer and fewer points. You have 60 seconds to hit all targets. When you finish the game will announce your time and score. Game Mode 3 =========== 1 player mode. The object is to hit all 8 targets as quickly as possible. Points per target vary by sensor. This mode is intended for placing 24oz cans on the top middle sensors, 16oz cans on the bottom middle sensors, 12oz cans on the top left and right sensors, and 7.5oz cans on the bottom left and right sensors. The smaller the can the more points it's worth, but as time runs the targets are worth fewer and fewer points. You have 60 seconds to hit all targets. When you finish the game will announce your time and score. Game Mode 4 =========== 1 player mode. Sharp shooting game mode. The game will announce targets to hit. If you his incorrect targets you will be penalized points. The targets to hit alternate from far top left to far lower right. So a perfect game would entail hitting targets 1, 8, 2, 7, 3, 6, 4, and 5, in that exact order. Points per target are the same, but as time runs the targets are worth fewer and fewer points. You have 60 seconds to hit all targets. When you finish the game will announce your time and score. Game Mode 5 =========== 2 player variant of game mode 1. The object is for each player to hit all 4 of their targets as quickly as possible. Player 1's targets are the 4 on the left side and player 2's targets are the 4 on the right side. The game stops when all 4 targets of one player are hit. The game will announce the winner and winning time. Game Mode 6 =========== 2 player version of game mode 2. The object is for each player to hit all 4 of their targets as quickly as possible. Player 1's targets are the 4 on the left side and player 2's targets are the 4 on the right side. Points per target are the same, but as time runs the targets are worth fewer and fewer points. Players have 60 seconds to hit all targets. When finished the game will announce the winning score. Game Mode 7 =========== 2 player version of game mode 3. The object is for each player to hit all 4 of their targets as quickly as possible. Player 1's targets are the 4 on the left side and player 2's targets are the 4 on the right side. Points per target vary by sensor. This mode is intended for placing 24oz cans on the top middle sensors, 16oz cans on the bottom middle sensors, 12oz cans on the top left and right sensors, and 7.5oz cans on the bottom left and right sensors. The smaller the can the more points it's worth, but as time runs the targets are worth fewer and fewer points. Players have 60 seconds to hit all targets. When finished the game will announce the winning score. Game Mode 8 =========== 2 player version of game mode 4. Sharp shooting game mode. Player 1 goes first, then player 2. The game will announce targets to hit. If you his incorrect targets you will be penalized points. The targets to hit alternate from far top left to far lower right. So a perfect game would entail hitting targets 1, 8, 2, 7, 3, 6, 4, and 5, in that exact order. Points per target are the same, but as time runs the targets are worth fewer and fewer points. You have 60 seconds to hit all targets. When both players have completed their rounds the game will announce the winner, scores, and missed targets. The game will power down on its own if setting idle for 4 minutes. After 3 minutes it will warn about shutting down. If a USB flash drive is plugged into the Raspberry Pi when it boots that drive will be mounted as /mnt/usb and the C program will load and save "config.txt" and "gamelog.txt" to that drive. The "config.txt" file contains some basic game options, as follows... 30 <--- Shooting distance. Configureable in-game or by editing "config.txt". Must be a whole number 1-100. 1 <--- Flite voice to use. 1: slt, 2: kal16 Configuring the C program to add other voice options would be fairly easy to do. 30.000000 <--- Base shooting distance, which is the distance at which the scoring coefficient (a.k.a. points multiplier) is 1 (1x). Change to your liking. It is a floating point number. 2.000000 <--- Exponent of the scoring coefficient (sce) formula. sce = (shooting distance / base distance)^exponent. Change to your liking. It is a floating point number. As for "gamelog.txt", that is the log file for the game. In includes timestamped game start times (timestamps accurate assuming your Raspberry Pi is connected to the Internet or you've installed a RTC), player names, game modes, times, and scores. If a USB flash drive is not detected when the Raspberry Pi powers on then the C program will work from "config.txt" and "gamelog.txt" files in the home folder, /home/user, residing on the SD card. Alas, it can be handy to plug-in a USB flash drive for easily grabbing game results without needing to SSH into the system.